import asyncio
from common.exchange import get_async_binance_portfolio_exchange
from copy import deepcopy
from datetime import datetime
import threading
from trade.broker.base import BrokerBase
from typing import Iterator, List


class BrokerReal(BrokerBase):
    bar_slider: Iterator

    def __init__(
        self,
        symbols: List,
        leverages: dict,
    ):
        self.exchange = get_async_binance_portfolio_exchange()
        self.symbols = symbols
        self.leverages = deepcopy(leverages)
        self.init_sig = threading.Semaphore()
        th = threading.Thread(target=asyncio.run, args=(self.init_datas(),))
        th.start()
        self.init_sig.acquire()

    def get_positions(self):
        return deepcopy(self.positions)

    def get_balances(self):
        return self.balances

    def get_cur_time(self):
        return datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]

    async def init_datas(self):
        await self.exchange.load_markets()
        self.markets = deepcopy(self.exchange.markets)
        for symbol, leverage in self.leverages.items():
            if symbol in self.markets and self.markets[symbol]["type"] == "future":
                await self.exchange.set_leverage(leverage, symbol)

        balances = await self.exchange.fetch_balance({"type": "margin"})
        keys = balances["total"].keys()
        self.balances = {x: y for x, y in balances.items() if x in keys}

        self.balances_lock = asyncio.Lock()

        self.positions = {}
        positions = await self.exchange.fetch_positions()
        for x in positions:
            self.positions[x["symbol"]] = x
        self.positions_lock = asyncio.Lock()
        self.init_sig.release()
        task1 = asyncio.create_task(self.watch_balances())
        task2 = asyncio.create_task(self.watch_positions())
        await asyncio.gather(task1, task2)

    async def watch_balances(self):
        print("start watch balance")
        while True:
            await self.exchange.watch_balance()
            balances = await self.exchange.fetch_balance(params={"type": "margin"})
            await self.balances_lock.acquire()
            keys = balances["total"].keys()
            self.balances = {x: y for x, y in balances.items() if x in keys}
            self.balances_lock.release()

    async def watch_positions(self):
        print("start watch position")
        while True:
            positions = await self.exchange.watch_positions(
                self.symbols
            )  # todo: 不一定所有symbol都监控了
            await self.positions_lock.acquire()
            for x in positions:
                self.positions[x["symbol"]] = x
            self.positions_lock.release()

    def order_future(
        self,
        symbol: str,
        size: int,
        price: float,
        timeInForce: str,
        market_type: str,
    ) -> bool:
        print(f"order future: {(symbol, size, price, timeInForce, market_type)}")
        if symbol in self.positions and self.positions[symbol]["side"]:
            position_side = self.positions[symbol]["side"]
        else:
            position_side = self._direction_to_side()
        params = {
            # 'timeInForce': timeInForce,  # GTC, IOC, FOK
            "positionSide": position_side,
        }
        if market_type == "limit":
            params["timeInForce"] = timeInForce

        asyncio.get_event_loop().run_until_complete(
            self.exchange.create_order(
                symbol,
                market_type,
                amount=size,
                price=price,
                params=params,
            )
        )
        return True

    def order_spot(
        self,
        symbol: str,
        size: int,
        price: float,
        timeInForce: str,
        market_type: str,
    ) -> bool:
        print(f"order future: {(symbol, size, price, timeInForce, market_type)}")
        params = {}
        if market_type == "limit":
            params["timeInForce"] = timeInForce

        asyncio.get_event_loop().run_until_complete(
            self.exchange.create_order(
                symbol,
                market_type,
                amount=size,
                price=price,
                params=params,
            )
        )
        return True
